package Image;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import com.jhlabs.image.*;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;

/**
 * An Icon which can be scaled in various ways as well as automatically to its container.
 *
 * I created this class a while ago for a PSP skinning program and ported it to this app
 * to display our images.  Not all the bugs are worked out but it functions well enough.
 *
 * @author Seth Wessitsh <seth@wessitsh.com>
 */
public class ScaledIcon extends ImageIcon {
	public static final long serialVersionUID = 1L;

	double scale = 1.0;
	int scaleHint = Image.SCALE_SMOOTH;
	boolean scaleToContainer = false;
	boolean autoScaled = false;
	AbstractBufferedImageOp renderingOp = null;
	BufferedImage offImage;
	Graphics2D offGraphics;
	Component container = null;

	public ScaledIcon(double scale) {
		super();
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(byte[] imageData, double scale, String description) {
		super(imageData, description);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(byte[] imageData) {
		super(imageData);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(String path) {
		super(path);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(String path, String description) {
		super(path, description);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(Image image, double scale, String description) {
		super(image, description);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(Image image, double scale) {
		super(image);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon (Image image) {
		super(image);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon (Image image, String description) {
		super(image, description);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(String filename, double scale, String description) {
		super(filename, description);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(String filename, double scale) {
		super(filename);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(URL location, double scale, String description) {
		super(location, description);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(URL location, double scale) {
		super(location);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(URL location) {
		super(location);
		try {
			setScale(scale);
		} catch (Throwable t) {}
	}

	public ScaledIcon(URL location, int width) {
		super(location);
		try {
			setScaleFromWidth(width);
		} catch (Throwable t) {}
	}

	public double getScale() {
		return scale;
	}

	@Override
	public int getIconHeight() {
		if (scaleToContainer && container != null) 
			setScaleFromSize(container.getWidth(), container.getHeight());

		return getWidth();
	}

	@Override
	public int getIconWidth() {
		if (scaleToContainer && container != null) 
			setScaleFromSize(container.getWidth(), container.getHeight());

		return getHeight();
	}

	public int getWidth() {
		if (scaleToContainer && container != null) 
			setScaleFromSize(container.getWidth(), container.getHeight());

		return (int) (scale*getImage().getWidth(null)) > 0 ? 
			(int) (scale*getImage().getWidth(null)) : 1;
	}

	public int getHeight() {
		if (scaleToContainer && container != null) 
			setScaleFromSize(container.getWidth(), container.getHeight());

		return (int) (scale*getImage().getHeight(null)) > 0 ? 
			(int) (scale*getImage().getHeight(null)) : 1;
	}

	public Image getScaledImage() {
		return getImage().getScaledInstance(getWidth(), getHeight(), scaleHint);
	}

	public double setScale(double scale) throws IllegalArgumentException {
		double retVal = this.scale;
		if (scale >= 0) {
			this.scale = scale;
		} else {
			throw new IllegalArgumentException();
		}
		return retVal;
	}

	public void setContainer(Component c) {
		container = c;
	}

	public double setScaleFromWidth(int width) throws IllegalArgumentException {
		if (width >= 0) {
			double retVal = (double) width / (double) getImage().getWidth(null);
			this.scale = retVal;
			return retVal;
		} else {
			throw new IllegalArgumentException();
		}
	}

	public double setScaleFromHeight(int height) throws IllegalArgumentException {
		if (height >= 0) {
			double retVal = (double) height / (double) getImage().getHeight(null);
			this.scale = retVal;
			return retVal;
		} else {
			throw new IllegalArgumentException();
		}
	}
	
	public double setScaleFromSize(int width, int height) {
		double imageAspect = 
			(double) getImage().getWidth(null) / (double) getImage().getHeight(null);
		double containerAspect = (double) width / (double) height;

		if (imageAspect > containerAspect) {
			setScaleFromWidth(width);
		} else if (imageAspect < containerAspect) {
			setScaleFromHeight(height);
		} else {
			setScaleFromWidth(width);
		}

		return getScale();
	}

	public int getScaleHint() {
		return scaleHint;
	}

	public int setScaleHint(int scaleHint) throws IllegalArgumentException {
		if (scaleHint == Image.SCALE_AREA_AVERAGING ||
			scaleHint == Image.SCALE_DEFAULT ||
			scaleHint == Image.SCALE_FAST ||
			scaleHint == Image.SCALE_REPLICATE ||
			scaleHint == Image.SCALE_SMOOTH) 
		{
			int retVal = this.scaleHint;
			this.scaleHint = scaleHint;
			return retVal;
		} else {
			throw new IllegalArgumentException();
		}
	}
	
	public void setRenderingOp(AbstractBufferedImageOp op) {
		renderingOp = op;
	}

	public boolean isScaledToContainer() {
		return scaleToContainer;
	}

	public void setScaleToContainer(boolean scaleToContainer) {
		this.scaleToContainer = scaleToContainer;
	}

	@Override
	public void paintIcon(Component c, Graphics g, int x, int y) {
		if (container == null) container = c;
		if (scaleToContainer) {
			setScaleFromSize(c.getWidth(), c.getHeight());
			x = (c.getWidth()-getWidth())/2;
			y = (c.getHeight()-getHeight())/2;
		}
		
		int w = getImage().getWidth(null),
			h = getImage().getHeight(null),
			sw = getWidth(), 
		    sh = getHeight();
		Graphics2D g2 = (Graphics2D)g;

		if (renderingOp != null) {
			offImage = new BufferedImage(sw,sh,BufferedImage.TYPE_INT_RGB);
			offGraphics = (Graphics2D) offImage.getGraphics();
			BufferedImage opImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
			Graphics2D opGraphics = (Graphics2D) opImage.getGraphics();

			opGraphics.setRenderingHint(
				RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR
			);
			offGraphics.setRenderingHint(
				RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR
			);

			// Draw the image into a buffered image
			opGraphics.drawImage(getImage(), 0, 0, w, h, null);
			// Perform the rendering op
			opGraphics.drawImage(opImage, renderingOp, 0, 0);
			// Scale the image off screen
			offGraphics.drawImage(opImage, 0, 0, sw, sh, null);
		} else {
			offImage = new BufferedImage(sw,sh,BufferedImage.TYPE_INT_RGB);
			offGraphics = (Graphics2D) offImage.getGraphics();
			offGraphics.setRenderingHint(
				RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR
			);
			offGraphics.drawImage(getImage(), 0, 0, sw, sh, null);
		}
		g2.drawImage(offImage, x, y, sw, sh, c);
	}

	public static void main(String args[]) {
		JFrame frame = new JFrame("Scaled Icon Test");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JLabel label = new JLabel();
		URL imageUrl = ScaledIcon.class.getResource("/images/Image1.jpg");
		if (imageUrl == null) {
			System.out.println("Can't find image.");
		} else {
			ScaledIcon icon = new ScaledIcon(imageUrl, 0.2);
			icon.setScaleToContainer(true);
			label.setHorizontalAlignment(SwingConstants.CENTER);
			label.setVerticalAlignment(SwingConstants.CENTER);
//			icon.setRenderingOp(new BlurFilter());
			label.setIcon(icon);
			frame.getContentPane().add(label);
			frame.setMinimumSize(new Dimension(icon.getWidth(), icon.getHeight()));
		}
		frame.setVisible(true);
	}
}
